package com.belazy.basics.auth.service.impl;

import com.belazy.basics.auth.constant.SecurityConstants;
import com.belazy.basics.auth.mapper.ILoginLogMapper;
import com.belazy.basics.auth.model.LoginLog;
import com.belazy.basics.auth.model.from.LoginFrom;
import com.belazy.basics.auth.model.vo.LoginVo;
import com.belazy.basics.auth.properties.AuthConfig;
import com.belazy.basics.auth.service.IAuthService;
import com.belazy.library.model.Result;
import com.belazy.library.model.enums.GrantTypeEnum;
import com.belazy.library.model.enums.SourceEnum;
import com.belazy.library.util.constant.StringConstant;
import com.belazy.library.util.file.ResourcesUtil;
import com.belazy.library.util.rsa.RsaUtil;
import com.belazy.library.web.util.IpUtils;
import com.belazy.library.web.util.OkHttpUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.PropertyNamingStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tangcp
 */
@Slf4j
@Service
public class AuthServiceImpl implements IAuthService {
    private static final String MSG_1 = "账号或密码错误,登录失败!";
    private static final String MSG_2 = "登录来源不属于系统!";
    private static final String MSG_3 = "验证码错误,登录失败!";
    private static final String MSG_4 = "JSON转换异常!";
    @Autowired
    private AuthConfig authConfig;
    @Resource
    private ILoginLogMapper iLoginLogMapper;
    @Autowired
    private OkHttpClient okHttpClient;
    @Autowired
    private ConsumerTokenServices consumerTokenServices;
    private PrivateKey privateKey;

    @PostConstruct
    public void init() {
        //初始化信息
        String key = ResourcesUtil.getResourcesByStream ("priKey.jks");
        try {
            privateKey = RsaUtil.getPrivateKey (key);
        } catch (Exception e) {
            privateKey = null;
            log.error ("PRIVATE_KEY init fail");
        }
    }

    @Override
    public Result<Boolean> logout(String token) {
        return consumerTokenServices.revokeToken (token) ? Result.success (true) : Result.fail ("token 失效!");
    }

    @Override
    public Result<LoginVo> login(LoginFrom from, HttpServletRequest request) {
        String username = from.getUsername ();
        String mGrantType = request.getHeader (SecurityConstants.GRANT_TYPE);
        if (StringUtils.isEmpty (mGrantType)) {
            mGrantType = from.getGrantType ();
            if (StringUtils.isEmpty (mGrantType)) {
                mGrantType = GrantTypeEnum.PASSWORD.val;
            }
        }
        from.setGrantType (mGrantType);
        // 检查请求来源
        if (SourceEnum.check (from.getSource ()) || GrantTypeEnum.check (mGrantType)) {
            return saveLoginLog (from, request, false, MSG_2);
        }
        if (StringUtils.isEmpty (username)) {
            return saveLoginLog (from, request, false, MSG_1);
        }
        boolean isSmsCode = GrantTypeEnum.SMS_CODE.val.equals (mGrantType);
        if (isSmsCode && StringUtils.isEmpty (from.getSmsCode ())) {
            return saveLoginLog (from, request, false, MSG_3);
        }
        if (!isSmsCode && StringUtils.isEmpty (from.getPassword ())) {
            return saveLoginLog (from, request, false, MSG_1);
        }

        String result = postRequest (from);
        log.info ("result==>{}", result);
        if (StringUtils.isEmpty (result)) {
            return saveLoginLog (from, request, false, MSG_1);
        }

        //处理OAuth2结果集
        ObjectMapper mapper = new ObjectMapper ();
        //忽略匹配不是的字段
        mapper.configure (DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //下滑线转驼峰
        mapper.setPropertyNamingStrategy (PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
        try {
            JsonNode jsonNode = mapper.readTree (result);
            if (null != jsonNode) {
                JsonNode statusNode = jsonNode.get ("status");
                if (null != statusNode) {
                    JsonNode messageNode = jsonNode.get ("message");
                    String message = messageNode.asText ();
                    String status = statusNode.asText ();
                    saveLoginLog (from, request, false, message);
                    return Result.fail (status, message);
                }
            }
            LoginVo loginInfoVo = mapper.readValue (result, LoginVo.class);
            saveLoginLog (from, request, true);
            return Result.success (loginInfoVo);
        } catch (IOException e) {
            saveLoginLog (from, request, false, MSG_4);
            return Result.unauthorized ();
        }
    }

    private String postRequest(LoginFrom in) {
        Map<String, String> param = new HashMap<> (5);
        param.put (SecurityConstants.GRANT_TYPE, in.getGrantType ());
        param.put (SecurityConstants.CLIENT_ID, authConfig.getClientId ());
        param.put (SecurityConstants.CLIENT_SECRET, authConfig.getClientSecret ());
        param.put (SecurityConstants.USERNAME, in.getUsername ());
        if (GrantTypeEnum.SMS_CODE.val.equals (in.getGrantType ())) {
            param.put (SecurityConstants.SMS_CODE, in.getSmsCode ());
        } else {
            String pwd = RsaUtil.decryptData (in.getPassword (), StringConstant.CHARSET_UTF_8,privateKey);
            param.put (SecurityConstants.PASSWORD, pwd);
        }
        log.info ("param==>{}",param);
        return OkHttpUtil.builder ().okHttpClient (okHttpClient).build ().post (authConfig.getAuthUri (), param);
    }

    private Result saveLoginLog(LoginFrom in, HttpServletRequest request, boolean isOk, String... msg) {
        LoginLog mLog = new LoginLog ();
        String browserName = IpUtils.getBrowserName (request);
        String browserVersion = IpUtils.getBrowserVersion (request);
        String ip = IpUtils.getIpAddr (request);
        String os = IpUtils.getOsName (request);
        String message = msg != null && msg.length > 0 ? msg[0] : "登录成功";
        String browser = SourceEnum.WEB.source.equals (in.getSource ()) ? browserName + " " + browserVersion : in.getBrowser ();
        String deviceId = SourceEnum.WEB.source.equals (in.getSource ()) ? IpUtils.getMac (ip) : in.getDeviceId ();
        String status = isOk ? "0" : "1";
        mLog.setIpaddr (ip);
        mLog.setOs (os);
        mLog.setSource (in.getSource ());
        mLog.setStatus (status);
        mLog.setUserName (in.getUsername ());
        mLog.setDeviceId (deviceId);
        mLog.setBrowser (browser);
        mLog.setVersion (in.getVersion ());
        mLog.setCreateBy (in.getUsername ());
        mLog.setUpdateBy (in.getUsername ());
        mLog.setMsg (message);
        iLoginLogMapper.insertLoginLog (mLog);
        return isOk ? Result.success () : Result.fail (message);
    }
}
